package cn.org.atool.fluent.mybatis.metadata.feature;

import lombok.Getter;

import java.util.Objects;

import static java.lang.String.format;

/**
 * 各类型数据库的分页方式
 * 格式由4个变量组成, 其中{query}是必须变量, {offset}/{size}/{end} 这3个量选2个
 * <p>
 * o {query}, 查询sql语句主体
 * o {offset}, 分页开始位移
 * o {size}, 分页查询数量
 * o {end}, 分页结束位移
 *
 * @author darui.wu
 */
public class PagedFormat {
    private final static int SELECT_LENS = "SELECT".length();

    @Getter
    protected final String format;

    public PagedFormat(String format) {
        this.format = format;
    }

    /**
     * 不支持分页
     */
    public static final PagedFormat UN_SUPPORT_LIMIT = new PagedFormat(null);
    /**
     * MYSQL语法分页
     */
    public static final PagedFormat MYSQL_LIMIT = new PagedFormat("{query} LIMIT {offset}, {size}");
    /**
     * PG语法分页
     */
    public static final PagedFormat PG_LIMIT = new PagedFormat("{query} LIMIT {size} OFFSET {offset}");
    /**
     * HSQL分页
     */
    public static final PagedFormat HSQLDB_LIMIT = new PagedFormat("{query} OFFSET {offset} ROWS LIMIT {size}");
    /**
     * DB2语法分页
     */
    public static final PagedFormat DB2_LIMIT = new PagedFormat(
        "SELECT * FROM " +
            "(SELECT TMP_PAGE.*, ROW_NUMBER() OVER() AS RN FROM ({query}) AS TMP_PAGE) " +
            "WHERE RN > {offset} AND RN <= {end}");

    /**
     * Informix分页
     */
    public static final PagedFormat INFORMIX_LIMIT = new PagedFormat(
        "SELECT SKIP %s FIRST %s %s"
    ) {
        @Override
        public String build(String query, String offset, String size, String endOffset) {
            String rest = startWithSelect(query);
            if (rest != null) {
                return format(format, offset, size, rest.trim());
            }
            throw new RuntimeException("paged not support for sql:" + query);
        }
    };
    /**
     * sql server 2005分页
     */
    public static final PagedFormat SQLSERVER2005_LIMIT = DB2_LIMIT;
    /**
     * Derby分页
     */
    public static final PagedFormat DERBY_LIMIT = new PagedFormat(
        "{query} OFFSET {offset} ROWS FETCH NEXT {size} ROWS ONLY");
    /**
     * sql server 2012分页
     */
    public static final PagedFormat SQLSERVER2012_LIMIT = DERBY_LIMIT;
    /**
     * Oracle 11g and less 语法分页
     */
    public static final PagedFormat ORACLE_LIMIT = new PagedFormat(
        "SELECT * FROM (" +
            "SELECT TMP_PAGE.*, ROWNUM RN FROM ({query}) TMP_PAGE) " +
            "WHERE RN > {offset} AND RN <= {end}");
    /**
     * ORACLE12语法分页
     */
    public static final PagedFormat ORACLE12_LIMIT = DERBY_LIMIT;
    /**
     * firebird语法分页
     */
    public static final PagedFormat FIREBIRD_LIMIT = new PagedFormat("{query} ROWS {offset} TO {end}");

    public String build(String query, String offset, String size, String endOffset) {
        if (format == null || format.trim().isEmpty()) {
            throw new RuntimeException("paged not support by dbType");
        }
        return this.format.replace("{end}", endOffset)
            .replace("{size}", size)
            .replace("{offset}", offset)
            .replace("{query}", query);
    }

    private static String startWithSelect(String query) {
        if (query.length() <= SELECT_LENS) {
            return null;
        }
        String select = query.substring(0, SELECT_LENS);
        if (Objects.equals(select.toUpperCase(), "SELECT")) {
            return query.substring(SELECT_LENS);
        } else {
            return null;
        }
    }
}